﻿' LibWowArmory
' by Ronald M. Clifford (roncli@roncli.com)
'
' This source code is released under the GNU Library General Public License (LGPL) Version 2.1.

Imports roncliProductions.LibWowArmory
Imports roncliProductions.LibWowArmory.Character
Imports roncliProductions.LibWowArmory.Character.Reputation
Imports roncliProductions.LibWowArmory.Character.Talents
Imports System
Imports System.Collections.ObjectModel
Imports System.Drawing
Imports System.Globalization
Imports System.IO
Imports System.Linq
Imports System.Text.RegularExpressions
Imports System.Web
Imports System.Xml.Serialization

Namespace roncliProductions.LibWowArmory.Character.Sheet

    ''' <summary>
    ''' A class that gets a character's information from the Armory
    ''' </summary>
    ''' <remarks></remarks>
    Public Class CharacterSheet
        Inherits WowArmoryData

        Private csPage As New Schema.page
        Private lstAchievements As New Collection(Of Achievements)

#Region "Properties"

        ''' <summary>
        ''' The realm the character is on
        ''' </summary>
        ''' <value></value>
        ''' <returns></returns>
        ''' <remarks></remarks>
        Public Property Realm As String

        ''' <summary>
        ''' The name of the character
        ''' </summary>
        ''' <value></value>
        ''' <returns></returns>
        ''' <remarks></remarks>
        Public Property Name As String

        ''' <summary>
        ''' The URL that will be used to retrieve the character from the Armory
        ''' </summary>
        ''' <value></value>
        ''' <returns></returns>
        ''' <remarks></remarks>
        Public Overrides ReadOnly Property URL As String
            Get
                Return String.Format(CultureInfo.InvariantCulture, "http://www.wowarmory.com/character-sheet.xml?r={0}&n={1}", Realm, Name)
            End Get
        End Property

        ''' <summary>
        ''' Indicates to the inherited class that this request does not require login
        ''' </summary>
        ''' <value></value>
        ''' <returns></returns>
        ''' <remarks></remarks>
        Public Overrides ReadOnly Property RequiresLogin As Boolean
            Get
                Return False
            End Get
        End Property

        ''' <summary>
        ''' The battlegroup the character is a part of
        ''' </summary>
        ''' <value></value>
        ''' <returns></returns>
        ''' <remarks></remarks>
        Public ReadOnly Property BattleGroup As String
            Get
                Return GetCharacter.battleGroup
            End Get
        End Property

        ''' <summary>
        ''' The character's class
        ''' </summary>
        ''' <value></value>
        ''' <returns></returns>
        ''' <remarks></remarks>
        Public ReadOnly Property [Class] As Enums.ClassType
            Get
                Return CType(GetCharacter.classId, Enums.ClassType)
            End Get
        End Property

        ''' <summary>
        ''' The character's faction
        ''' </summary>
        ''' <value></value>
        ''' <returns></returns>
        ''' <remarks></remarks>
        Public ReadOnly Property Faction As Enums.FactionType
            Get
                Return CType(GetCharacter.factionId, Enums.FactionType)
            End Get
        End Property

        ''' <summary>
        ''' The character's gender
        ''' </summary>
        ''' <value></value>
        ''' <returns></returns>
        ''' <remarks></remarks>
        Public ReadOnly Property Gender As Enums.GenderType
            Get
                Return CType(GetCharacter.genderId, Enums.GenderType)
            End Get
        End Property

        ''' <summary>
        ''' The name of the character's guild
        ''' </summary>
        ''' <value></value>
        ''' <returns></returns>
        ''' <remarks></remarks>
        Public ReadOnly Property GuildName As String
            Get
                Return GetCharacter.guildName
            End Get
        End Property

        ''' <summary>
        ''' The date this character was last modified on the Armory
        ''' </summary>
        ''' <value></value>
        ''' <returns></returns>
        ''' <remarks></remarks>
        Public ReadOnly Property LastModified As Date
            Get
                Return CDate(GetCharacter.lastModified)
            End Get
        End Property

        ''' <summary>
        ''' The character's level
        ''' </summary>
        ''' <value></value>
        ''' <returns></returns>
        ''' <remarks></remarks>
        Public ReadOnly Property Level As Integer
            Get
                Return GetCharacter.level
            End Get
        End Property

        ''' <summary>
        ''' The character's overall progress in achievements
        ''' </summary>
        ''' <value></value>
        ''' <returns></returns>
        ''' <remarks></remarks>
        Public ReadOnly Property AchievementProgress As Achievements
            Get
                Dim cCategory As Schema.c = csPage.characterInfo.First.summary.First.c.First
                Return New Achievements("Overall", cCategory.earned, cCategory.points, cCategory.total, cCategory.totalPoints)
            End Get
        End Property

        ''' <summary>
        ''' The character's progress in achievements by category
        ''' </summary>
        ''' <value></value>
        ''' <returns></returns>
        ''' <remarks></remarks>
        Public ReadOnly Property AchievementCategories As Collection(Of Achievements)
            Get
                Return lstAchievements
            End Get
        End Property

        ''' <summary>
        ''' The number of achievement points the character has
        ''' </summary>
        ''' <value></value>
        ''' <returns></returns>
        ''' <remarks></remarks>
        Public ReadOnly Property AchievementPoints As Integer
            Get
                Return GetCharacter.points
            End Get
        End Property

        ''' <summary>
        ''' The character's prefix
        ''' </summary>
        ''' <value></value>
        ''' <returns></returns>
        ''' <remarks></remarks>
        Public ReadOnly Property Prefix As String
            Get
                Return GetCharacter.prefix
            End Get
        End Property

        ''' <summary>
        ''' The character's race
        ''' </summary>
        ''' <value></value>
        ''' <returns></returns>
        ''' <remarks></remarks>
        Public ReadOnly Property Race As Enums.RaceType
            Get
                Return CType(GetCharacter.raceId, Enums.RaceType)
            End Get
        End Property

        ''' <summary>
        ''' The character's suffix
        ''' </summary>
        ''' <value></value>
        ''' <returns></returns>
        ''' <remarks></remarks>
        Public ReadOnly Property Suffix As String
            Get
                Return GetCharacter.suffix
            End Get
        End Property

        ''' <summary>
        ''' The character's arena teams
        ''' </summary>
        ''' <value></value>
        ''' <returns></returns>
        ''' <remarks></remarks>
        Public ReadOnly Property ArenaTeams As Collection(Of ArenaTeam)
            Get
                Dim cCharacter As Schema.characterInfo_character = GetCharacter()
                If cCharacter.arenaTeams.Count = 0 Then
                    Return New Collection(Of ArenaTeam)
                Else
                    Return (From t In cCharacter.arenaTeams.First.arenaTeam Select New ArenaTeam((From m In t.members.First.character Select New ArenaTeamMember(CType(m.classId, Enums.ClassType), m.contribution, m.gamesPlayed, m.gamesWon, CType(m.genderId, Enums.GenderType), m.guild, m.name, CType(m.raceId, Enums.RaceType), m.seasonGamesPlayed, m.seasonGamesWon, CBool(1 - m.teamRank))).ToCollection, t.created, t.gamesPlayed, t.gamesWon, t.lastSeasonRanking, t.name, t.ranking, t.rating, t.seasonGamesPlayed, t.seasonGamesWon, t.size, t.teamUrl, GetColorFromHex(t.emblem.First.background), GetColorFromHex(t.emblem.First.borderColor), t.emblem.First.borderStyle, GetColorFromHex(t.emblem.First.iconColor), t.emblem.First.iconStyle)).ToCollection
                End If
            End Get
        End Property

        ''' <summary>
        ''' Get the primary talent spec
        ''' </summary>
        ''' <value></value>
        ''' <returns></returns>
        ''' <remarks></remarks>
        Public ReadOnly Property PrimaryTalentSpec As String
            Get
                Return GetCharacterTab.talentSpecs.First.talentSpec.Where(Function(ts) ts.group = 1).First.prim
            End Get
        End Property

        ''' <summary>
        ''' The number of talent points the player has put into their primary talent tree
        ''' </summary>
        ''' <param name="intTree">The number of the talent tree, 1, 2, or 3.  Talent trees are always ordered from left to right on the talent sheet, and are always ordered in alphabetical order.</param>
        ''' <value></value>
        ''' <returns></returns>
        ''' <remarks></remarks>
        Public ReadOnly Property PrimaryTalentPoints(ByVal intTree As Integer) As Integer
            Get
                Dim ctCharacter As Schema.characterTab = GetCharacterTab
                Dim tsTalentSpec As Schema.talentSpec = ctCharacter.talentSpecs.First.talentSpec.Where(Function(ts) ts.group = 1).First
                Select Case intTree
                    Case 1
                        Return tsTalentSpec.treeOne
                    Case 2
                        Return tsTalentSpec.treeTwo
                    Case 3
                        Return tsTalentSpec.treeThree
                    Case Else
                        Throw New ArgumentException("Can only select talent tree 1, 2, or 3.")
                End Select
            End Get
        End Property

        ''' <summary>
        ''' Get the secondary talent spec
        ''' </summary>
        ''' <value></value>
        ''' <returns></returns>
        ''' <remarks></remarks>
        Public ReadOnly Property SecondaryTalentSpec As String
            Get
                Dim ctCharacter As Schema.characterTab = GetCharacterTab

                If ctCharacter.talentSpecs.First.talentSpec.Count < 2 Then
                    Return "n/a"
                Else
                    Return ctCharacter.talentSpecs.First.talentSpec.Where(Function(ts) ts.group = 2).First.prim
                End If
            End Get
        End Property

        ''' <summary>
        ''' The number of talent points the player has put into their secondary talent tree
        ''' </summary>
        ''' <param name="intTree">The number of the talent tree, 1, 2, or 3.  Talent trees are always ordered from left to right on the talent sheet, and are always ordered in alphabetical order.</param>
        ''' <value></value>
        ''' <returns></returns>
        ''' <remarks></remarks>
        Public ReadOnly Property SecondaryTalentPoints(ByVal intTree As Integer) As Integer
            Get
                Dim ctCharacter As Schema.characterTab = GetCharacterTab

                If ctCharacter.talentSpecs.First.talentSpec.Count < 2 Then
                    Throw New ArgumentException("This character has not dual speced yet.")
                End If

                Dim tsTalentSpec As Schema.talentSpec = ctCharacter.talentSpecs.First.talentSpec.Where(Function(ts) ts.group = 2).First
                Select Case intTree
                    Case 1
                        Return tsTalentSpec.treeOne
                    Case 2
                        Return tsTalentSpec.treeTwo
                    Case 3
                        Return tsTalentSpec.treeThree
                    Case Else
                        Throw New ArgumentException("Can only select talent tree 1, 2, or 3.")
                End Select
            End Get
        End Property

        ''' <summary>
        ''' The number of lifetime honorable kills the character has
        ''' </summary>
        ''' <value></value>
        ''' <returns></returns>
        ''' <remarks></remarks>
        Public ReadOnly Property LifetimeHonorKills As Integer
            Get
                Return GetCharacterTab.pvp.First.lifetimehonorablekills.First.value
            End Get
        End Property

        ''' <summary>
        ''' The number of arena points the character has
        ''' </summary>
        ''' <value></value>
        ''' <returns></returns>
        ''' <remarks></remarks>
        Public ReadOnly Property ArenaPoints As Integer
            Get
                Return GetCharacterTab.pvp.First.arenacurrency.First.value
            End Get
        End Property

        ''' <summary>
        ''' The professions known by the character
        ''' </summary>
        ''' <value></value>
        ''' <returns></returns>
        ''' <remarks></remarks>
        Public ReadOnly Property Professions As Collection(Of Profession)
            Get
                Dim cCharacterTab As Schema.characterTab = GetCharacterTab()
                If cCharacterTab.professions.Count = 0 Then
                    Return New Collection(Of Profession)
                Else
                    Return (From s In cCharacterTab.professions.First.skill Select New Profession(s.id, s.max, s.name, s.value)).ToCollection
                End If
            End Get
        End Property

        ''' <summary>
        ''' The identification number of the character's current title
        ''' </summary>
        ''' <value></value>
        ''' <returns></returns>
        ''' <remarks></remarks>
        Public ReadOnly Property TitleID As Integer
            Get
                Dim cCharacterInfo As Schema.characterInfo_character = GetCharacter
                If cCharacterInfo Is Nothing Then
                    Return Nothing
                Else
                    Return cCharacterInfo.titleId
                End If
            End Get
        End Property

        ''' <summary>
        ''' The amount of health the character has
        ''' </summary>
        ''' <value></value>
        ''' <returns></returns>
        ''' <remarks></remarks>
        Public ReadOnly Property Health As Integer
            Get
                Dim cbCharacterBars As Schema.characterBars = GetCharacterBars()
                If cbCharacterBars Is Nothing Then
                    Return Nothing
                Else
                    If cbCharacterBars.health.Count = 0 Then
                        Return Nothing
                    Else
                        Return cbCharacterBars.health.First.effective
                    End If
                End If
            End Get
        End Property

        ''' <summary>
        ''' The amount of mana, rage, energy, or runic power the character has
        ''' </summary>
        ''' <value></value>
        ''' <returns></returns>
        ''' <remarks></remarks>
        Public ReadOnly Property Power As Integer
            Get
                Dim sbSecondBar As Schema.secondBar = GetSecondBar()
                If sbSecondBar Is Nothing Then
                    Return Nothing
                Else
                    Return sbSecondBar.effective
                End If
            End Get
        End Property

        ''' <summary>
        ''' The type of power that the character has
        ''' </summary>
        ''' <value></value>
        ''' <returns></returns>
        ''' <remarks></remarks>
        Public ReadOnly Property PowerType As String
            Get
                Dim sbSecondBar As Schema.secondBar = GetSecondBar()
                If sbSecondBar Is Nothing Then
                    Return Nothing
                Else
                    Select Case sbSecondBar.type
                        Case "m"
                            Return "Mana"
                        Case "r"
                            Return "Rage"
                        Case "e"
                            Return "Energy"
                        Case "p"
                            Return "Runic Power"
                        Case Else
                            Return Nothing
                    End Select
                End If
            End Get
        End Property

        ''' <summary>
        ''' The character's base stats
        ''' </summary>
        ''' <value></value>
        ''' <returns></returns>
        ''' <remarks></remarks>
        Public ReadOnly Property Stats As BaseStats
            Get
                Dim ctCharacterTab As Schema.characterTab = GetCharacterTab()
                If ctCharacterTab Is Nothing Then
                    Return Nothing
                Else
                    Return (From b In ctCharacterTab.baseStats Select New BaseStats(
                        (From s In b.strength Select New StrengthStats(s.attack, s.base, s.block, s.effective)).First,
                        (From a In b.agility Select New AgilityStats(a.armor, a.attack, a.base, a.critHitPercent, a.effective)).First,
                        (From s In b.stamina Select New StaminaStats(s.base, s.effective, s.health, s.petBonus)).First,
                        (From i In b.intellect Select New IntellectStats(i.base, i.critHitPercent, i.effective, i.mana, i.petBonus)).First,
                        (From s In b.spirit Select New SpiritStats(s.base, s.effective, s.healthRegen, s.manaRegen)).First,
                        (From a In b.armor Select New ArmorStats(a.base, a.effective, a.percent, a.petBonus)).First)).First
                End If
            End Get
        End Property

        ''' <summary>
        ''' Information about the character's spell resistance
        ''' </summary>
        ''' <value></value>
        ''' <returns></returns>
        ''' <remarks></remarks>
        Public ReadOnly Property Resistances As ResistanceStats
            Get
                Dim ctCharacterTab As Schema.characterTab = GetCharacterTab()
                If ctCharacterTab Is Nothing Then
                    Return Nothing
                Else
                    Return (From r In ctCharacterTab.resistances Select New ResistanceStats(
                            (From a In r.arcane Select New Resistance(a.petBonus, a.value)).First,
                            (From f In r.fire Select New Resistance(f.petBonus, f.value)).First,
                            (From f In r.frost Select New Resistance(f.petBonus, f.value)).First,
                            (From h In r.holy Select New Resistance(h.petBonus, h.value)).First,
                            (From n In r.nature Select New Resistance(n.petBonus, n.value)).First,
                            (From s In r.shadow Select New Resistance(s.petBonus, s.value)).First)).First
                End If
            End Get
        End Property

        ''' <summary>
        ''' Information about the character's melee stats
        ''' </summary>
        ''' <value></value>
        ''' <returns></returns>
        ''' <remarks></remarks>
        Public ReadOnly Property Melee As MeleeStats
            Get
                Dim ctCharacterTab As Schema.characterTab = GetCharacterTab()
                If ctCharacterTab Is Nothing Then
                    Return Nothing
                Else
                    Return (From m In ctCharacterTab.melee Select New MeleeStats(
                            New WeaponStats(m.mainHandDamage.First.dps, m.mainHandDamage.First.max, m.mainHandDamage.First.min, m.mainHandDamage.First.percent, m.mainHandDamage.First.speed, m.mainHandSpeed.First.hastePercent, m.mainHandSpeed.First.hasteRating),
                            New WeaponStats(m.offHandDamage.First.dps, m.offHandDamage.First.max, m.offHandDamage.First.min, m.offHandDamage.First.percent, m.offHandDamage.First.speed, m.offHandSpeed.First.hastePercent, m.offHandSpeed.First.hasteRating),
                            (From p In m.power Select New MeleeAttackPowerStats(p.base, p.effective, p.increasedDPS)).First,
                            (From h In m.hitRating Select New PhysicalHitRatingStats(h.increasedHitPercent, h.penetration, h.reducedArmorPercent, h.value)).First,
                            (From c In m.critChance Select New PhysicalCritChanceStats(c.percent, c.plusPercent, c.rating)).First,
                            (From e In m.expertise Select New ExpertiseStats(e.additional, e.percent, e.rating, e.value)).First)).First
                End If
            End Get
        End Property

        ''' <summary>
        ''' Information about the character's ranged stats
        ''' </summary>
        ''' <value></value>
        ''' <returns></returns>
        ''' <remarks></remarks>
        Public ReadOnly Property Ranged As RangedStats
            Get
                Dim ctCharacterTab As Schema.characterTab = GetCharacterTab()
                If ctCharacterTab Is Nothing Then
                    Return Nothing
                Else
                    Return (From r In ctCharacterTab.ranged Select New RangedStats(
                            (From w In r.weaponSkill Select New WeaponSkillStats(w.rating, w.value)).First,
                            New WeaponStats(r.damage.First.dps, r.damage.First.max, r.damage.First.min, r.damage.First.percent, r.damage.First.speed, r.speed.First.hastePercent, r.speed.First.hasteRating),
                            (From p In r.power Select New RangedAttackPowerStats(p.base, p.effective, p.increasedDps, p.petAttack, p.petSpell)).First,
                            (From h In r.hitRating Select New PhysicalHitRatingStats(h.increasedHitPercent, h.penetration, h.reducedArmorPercent, h.value)).First,
                            (From c In r.critChance Select New PhysicalCritChanceStats(c.percent, c.plusPercent, c.rating)).First)).First
                End If
            End Get
        End Property

        ''' <summary>
        ''' Information about the character's spell stats
        ''' </summary>
        ''' <value></value>
        ''' <returns></returns>
        ''' <remarks></remarks>
        Public ReadOnly Property Spell As SpellStats
            Get
                Dim ctCharacterTab As Schema.characterTab = GetCharacterTab()
                If ctCharacterTab Is Nothing Then
                    Return Nothing
                Else
                    Return (From s In ctCharacterTab.spell Select New SpellStats(
                            New SpellPowerStats(s.bonusDamage.First.arcane.First.value, s.bonusDamage.First.fire.First.value, s.bonusDamage.First.frost.First.value, s.bonusDamage.First.holy.First.value, s.bonusDamage.First.nature.First.value, s.bonusDamage.First.shadow.First.value, s.bonusHealing.First.value),
                            s.bonusDamage.First.petBonus.First.attack,
                            s.bonusDamage.First.petBonus.First.damage,
                            s.bonusDamage.First.petBonus.First.fromType,
                            (From h In s.hitRating Select New SpellHitRatingStats(h.increasedHitPercent, h.penetration, h.reducedResist, h.value)).First,
                            (From c In s.critChance Select New SpellCritChanceStats(c.rating, c.arcane.First.percent, c.fire.First.percent, c.frost.First.percent, c.holy.First.percent, c.nature.First.percent, c.shadow.First.percent)).First,
                            s.manaRegen.First.casting,
                            s.manaRegen.First.notCasting,
                            s.hasteRating.First.hastePercent,
                            s.hasteRating.First.hasteRating)).First
                End If
            End Get
        End Property

        ''' <summary>
        ''' Information about the character's defensive stats
        ''' </summary>
        ''' <value></value>
        ''' <returns></returns>
        ''' <remarks></remarks>
        Public ReadOnly Property Defense As DefensiveStats
            Get
                Dim ctCharacterTab As Schema.characterTab = GetCharacterTab()
                If ctCharacterTab Is Nothing Then
                    Return Nothing
                Else
                    Return (From d In ctCharacterTab.defenses Select New DefensiveStats(
                            (From def In d.defense Select New DefenseStats(def.decreasePercent, def.increasePercent, def.plusDefense, def.rating, def.value)).First,
                            (From dod In d.dodge Select New AvoidanceStats(dod.increasePercent, dod.percent, dod.rating)).First,
                            (From p In d.parry Select New AvoidanceStats(p.increasePercent, p.percent, p.rating)).First,
                            (From b In d.block Select New AvoidanceStats(b.increasePercent, b.percent, b.rating)).First,
                            (From r In d.resilience Select New ResilienceStats(r.damagePercent, r.hitPercent, r.value)).First)).First
                End If
            End Get
        End Property

        ''' <summary>
        ''' The character's equipped items
        ''' </summary>
        ''' <value></value>
        ''' <returns></returns>
        ''' <remarks></remarks>
        Public ReadOnly Property Items As Collection(Of Item)
            Get
                Dim ctCharacterTab As Schema.characterTab = GetCharacterTab()
                If ctCharacterTab Is Nothing Then
                    Return New Collection(Of Item)
                Else
                    If ctCharacterTab.items Is Nothing Then
                        Return New Collection(Of Item)
                    Else
                        Return (From i In ctCharacterTab.items.First.item Select New Item(i.displayInfoId, i.durability, i.gem0Id, i.gem1Id, i.gem2Id, i.gemIcon0, i.gemIcon1, i.gemIcon2, i.icon, i.id, i.level, i.maxDurability, i.permanentEnchantIcon, i.permanentEnchantItemId, i.permanentenchant, i.pickUp, i.putDown, i.randomPropertiesId, CType(i.rarity, Enums.RarityType), i.seed, CType(i.slot, Enums.ItemSlotType))).ToCollection
                    End If
                End If
            End Get
        End Property

        ''' <summary>
        ''' The character's primary talents
        ''' </summary>
        ''' <value></value>
        ''' <returns></returns>
        ''' <remarks></remarks>
        Public ReadOnly Property PrimaryTalents As TalentTree.TalentTree
            Get
                Return New CharacterTalents(Realm, Name).PrimaryTalents
            End Get
        End Property

        ''' <summary>
        ''' The character's secondary talents
        ''' </summary>
        ''' <value></value>
        ''' <returns></returns>
        ''' <remarks></remarks>
        Public ReadOnly Property SecondaryTalents As TalentTree.TalentTree
            Get
                Return New CharacterTalents(Realm, Name).SecondaryTalents
            End Get
        End Property

        ''' <summary>
        ''' The character's list of faction reputations
        ''' </summary>
        ''' <value></value>
        ''' <returns></returns>
        ''' <remarks></remarks>
        Public ReadOnly Property Reputation As Collection(Of Reputation.Reputation)
            Get
                Return New CharacterReputation(Realm, Name).Reputation
            End Get
        End Property

#End Region

#Region "Public Methods"

        ''' <summary>
        ''' A constructor to load a character by realm and character name
        ''' </summary>
        ''' <param name="strRealm"></param>
        ''' <param name="strName"></param>
        ''' <remarks></remarks>
        Public Sub New(ByVal strRealm As String, ByVal strName As String)
            Realm = strRealm
            Name = strName
            Load()
        End Sub

        ''' <summary>
        ''' Loads the character from the Armory
        ''' </summary>
        ''' <remarks></remarks>
        Public Sub Load()
            Dim strKey As String = String.Format(CultureInfo.InvariantCulture, "LibWowArmory.CharacterSheet.{0}.{1}", Realm, Name)
            If Cache.Cache(strKey) Is Nothing Then
                MyBase.Retrieve()

                Dim xsSerializer As New XmlSerializer(csPage.GetType)

                Using srXML As New StringReader(MyBase.Data)
                    csPage = CType(xsSerializer.Deserialize(srXML), Schema.page)
                End Using

                Cache.Cache.Insert(strKey, csPage, Nothing, Date.UtcNow.AddDays(1), Nothing, Caching.CacheItemPriority.Normal, Nothing)
            Else
                csPage = CType(Cache.Cache(strKey), Schema.page)
            End If

            For Each cCategory As Schema.category In csPage.characterInfo.First.summary.First.category
                lstAchievements.Add(New Achievements(cCategory.name, cCategory.c.First.earned, cCategory.c.First.points, cCategory.c.First.total, cCategory.c.First.totalPoints))
            Next
        End Sub

#End Region

#Region "Private Methods"

        ''' <summary>
        ''' Returns basic character information
        ''' </summary>
        ''' <returns></returns>
        ''' <remarks></remarks>
        Private Function GetCharacter() As Schema.characterInfo_character
            Dim varCharacter = (From c In csPage.characterInfo Where c.character.Count > 0 Select c.character.First)
            If varCharacter.Count = 0 Then
                Throw New InvalidOperationException("Character not found.")
            End If
            Return varCharacter.First
        End Function

        ''' <summary>
        ''' Returns detailed character information
        ''' </summary>
        ''' <returns></returns>
        ''' <remarks></remarks>
        Private Function GetCharacterTab() As Schema.characterTab
            Dim varCharacterTab = (From c In csPage.characterInfo Where c.characterTab.Count > 0 Select c.characterTab.First)
            If varCharacterTab.Count = 0 Then
                Throw New InvalidOperationException("Character not found.")
            End If
            Return varCharacterTab.First
        End Function

        ''' <summary>
        ''' Returns the character's bars
        ''' </summary>
        ''' <returns></returns>
        ''' <remarks></remarks>
        Private Function GetCharacterBars() As Schema.characterBars
            Dim cCharacterTab As Schema.characterTab = GetCharacterTab()
            If cCharacterTab.characterBars.Count = 0 Then
                Return Nothing
            Else
                Return cCharacterTab.characterBars.First
            End If
        End Function

        ''' <summary>
        ''' Returns information on the character's power bar
        ''' </summary>
        ''' <returns></returns>
        ''' <remarks></remarks>
        Private Function GetSecondBar() As Schema.secondBar
            Dim cbCharacterBars As Schema.characterBars = GetCharacterBars
            If cbCharacterBars.secondBar.Count = 0 Then
                Return Nothing
            Else
                Return cbCharacterBars.secondBar.First
            End If
        End Function

        ''' <summary>
        ''' Retrieves the color from an 8 character ARGB hexidicmal string
        ''' </summary>
        ''' <param name="strColor"></param>
        ''' <returns></returns>
        ''' <remarks></remarks>
        Private Shared Function GetColorFromHex(ByVal strColor As String) As Color
            Dim mMatch As Match = Regex.Match(strColor, "([0-9a-fA-F]{2})([0-9a-fA-F]{2})([0-9a-fA-F]{2})([0-9a-fA-F]{2})")
            Return Color.FromArgb(Convert.ToInt32(mMatch.Groups(1).Value, 16), Convert.ToInt32(mMatch.Groups(2).Value, 16), Convert.ToInt32(mMatch.Groups(3).Value, 16), Convert.ToInt32(mMatch.Groups(4).Value, 16))
        End Function

#End Region

    End Class

End Namespace